#ifndef __M_BINDING_H__
#define __M_BINDING_H__
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"


namespace zymq
{
    struct Binding{
        using ptr = std::shared_ptr<Binding>;
        std::string exchange_name;
        std::string msgqueue_name;
        std::string binding_key;

        Binding(){}
        Binding(const std::string &ename, const std::string &qname, const std::string &key):
            exchange_name(ename), msgqueue_name(qname), binding_key(key){}
    };

    //队列与绑定信息是一一对应的（因为是给某个交换机去绑定队列，因此一个交换机可能会有多个队列的绑定信息）
    //因此先定义一个队列名，与绑定信息的映射关系，这个是为了方便通过队列名查找绑定信息
    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
    //然后定义一个交换机名称与队列绑定信息的映射关系，这个map中包含了所有的绑定信息，并且以交换机为单元进行了区分
    using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;

    class BindingMapper{
        public:
            BindingMapper(const std::string &dbfile):_sql_helper(dbfile){
                std::string ppath = FileHelper::parentDirectory(dbfile);
                FileHelper::createDirectory(ppath);
                assert(_sql_helper.open());
                createTable();
            }
            void createTable()
            {
                #define CREATE_BINDING_TABLE "create table if not exists binding_table(\
                exchange_name varchar(32),\
                msgqueue_name varchar(32),\
                binding_key varchar(128));"

                if(_sql_helper.exec(CREATE_BINDING_TABLE, nullptr, nullptr) == false)
                {
                    ELOG("创建绑定数据库表失败!!");
                    abort();
                }
            }
            void removeTable()
            {
                #define DROP_BINDING_TABLE "drop table if exists binding_table;"

                if(_sql_helper.exec(DROP_BINDING_TABLE, nullptr, nullptr) == false)
                {
                    ELOG("删除绑定数据库表表失败!!");
                }
            }
            bool insert(Binding::ptr &binding)
            {
                #define INSERT_BINDING_SQL "insert into binding_table values('%s', '%s', '%s');"
            
                char sql_str[4096] = {0};
                sprintf(sql_str, INSERT_BINDING_SQL, binding->exchange_name.c_str(), binding->msgqueue_name.c_str(), binding->binding_key.c_str());

                if(_sql_helper.exec(sql_str, nullptr, nullptr) == false)
                {
                    ELOG("新增数据失败!!");
                    return false;
                }
                return true;
            }
            void remove(const std::string &ename, const std::string &qname)
            {
                // delete from binding_table where exchange_name='' and msgqueue_name=''
                std::stringstream ss;
                ss << "delete from binding_table where ";
                ss << "exchange_name='" << ename << "' and ";
                ss << "msgqueue_name='" << qname << "';";

                if(_sql_helper.exec(ss.str(), nullptr, nullptr) == false)
                {
                    ELOG("删除数据失败!!");
                    return;
                }
            }
            void removeExchangeBindings(const std::string &ename)
            {
                std::stringstream ss;
                ss << "delete from binding_table where ";
                ss << "exchange_name='" << ename << "';";

                if(_sql_helper.exec(ss.str(), nullptr, nullptr) == false)
                {
                    ELOG("删除数据失败!!");
                    return;
                }
            }
            void removeMsgQueueBindings(const std::string &qname)
            {
                std::stringstream ss;
                ss << "delete from binding_table where ";
                ss << "msgqueue_name='" << qname << "';";

                if(_sql_helper.exec(ss.str(), nullptr, nullptr) == false)
                {
                    ELOG("删除数据失败!!");
                    return;
                }
            }
            BindingMap recovery()
            {
                // select exchange_name, msgqueue_name, binding_key from binding_table
                BindingMap ret;
                std::string sql = "select exchange_name, msgqueue_name, binding_key from binding_table;";

                _sql_helper.exec(sql, selectCallback, &ret);
                
                return ret;
            }
        private:
            static int selectCallback(void *arg, int col_count, char **result, char **fields_name)
            {
                BindingMap* pret = (BindingMap*)arg;
                Binding::ptr bp = std::make_shared<Binding>(result[0], result[1], result[2]);

                // 先获取交换机 所绑定的所有的队列信息，不存在就创建，存在就返回
                // 这里注意两点，第一点[]的作用，存在就返回，不存在创建
                // 第二点注意&，不加引用则修改的是栈上的临时变量，无法对BindingMap产生影响
                MsgQueueBindingMap &mqbm = (*pret)[bp->exchange_name];
                mqbm.insert({bp->msgqueue_name, bp});

                return 0;
            }
        private:
            SqliteHelper _sql_helper;
    };

    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;

        BindingManager(const std::string &dbfile):_mapper(dbfile)
        {
            // 当重新启动服务器时，从数据库中恢复数据到内存里
            _bindings = _mapper.recovery();
        }
        bool bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 有交换机绑定信息也有队列绑定信息则直接退出
            auto it = _bindings.find(ename);
            if(it != _bindings.end() && it->second.find(qname) != it->second.end()) return true;

            // 交换机是持久化&&队列是持久化再添加到数据库中
            // 而为了减少耦合（需要引入exchange和queue头文件），我们选择让外界传入durable
            Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
            if(durable) 
            {
                bool ret = _mapper.insert(bp);
                if(ret == false) return false;
            }

            auto &qbmap = _bindings[ename];
            qbmap.insert({qname, bp});
            return true;
        }
        void unBind(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 没有交换机绑定信息直接退出
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return;
            // 没有队列绑定信息直接退出
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return;

            _mapper.remove(ename, qname);
            _bindings[ename].erase(qname);
        }
        void removeExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            _mapper.removeExchangeBindings(ename);
            _bindings.erase(ename);
        }
        void removeMsgQueueBindings(const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            _mapper.removeMsgQueueBindings(qname);
            // 假设每一个交换机都有这个队列的绑定信息，则需要全部遍历一遍
            for(auto start = _bindings.begin(); start != _bindings.end(); ++start)
                start->second.erase(qname);
        }
        MsgQueueBindingMap getExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if( it == _bindings.end()) return MsgQueueBindingMap();
            return it->second;
        }
        Binding::ptr getBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 没有交换机绑定信息直接退出
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return Binding::ptr();
            // 没有队列绑定信息直接退出
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return Binding::ptr();

            return qit->second;
        }
        bool exists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 没有交换机绑定信息直接退出
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return false;
            // 没有队列绑定信息直接退出
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return false;

            return true;
        }
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            size_t total = 0;

            for(auto start = _bindings.begin(); start != _bindings.end(); ++start)
                total += start->second.size();

            return total;
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _bindings.clear();
        }
    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };
}

#endif